#include <cstdio>
#include <cstdlib>
//#include <tchar.h>

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/video/video.hpp"
#include <opencv2/gpu/gpu.hpp>


#include "VideoRead.h"

#include "FeatureComputer.h"

#include "Classifier.h"

#include "Bagging.h"

#include "Interface.h"

#define MAX_TRAIN_NUM 200000

int mainExtractFeat(LcInterface & my_setting);

int mainTrainWithExtract( LcInterface & my_setting );

int mainTestWithExtract( LcInterface & my_setting );

void getSampleSize( int rows, int n_frm, int & n_all , int & n_sample );

void getFeature(LcInterface & my_setting, Mat & desc_all, Mat & lab_all);

void getLabelMask( Mat & res, Mat & mask , Size size, int bound_setting);

int main(int argc, char * const argv[])
{
	cout << "hello" << endl;

	LcInterface my_setting;

	if( argc==1 )
	{
		//EXTRACT FEAT ONLY

		//char *const setting_string[] ={ "",
		//	"-a",
		//	"-s","./example_src.txt",
		//	"-g","./example_gt.txt",
		//	"-f","./example_feat.txt" ,
		//	"-v","zselect_id"};

		//my_setting.input( 1+1+8 , setting_string );

		//char *const setting_string[] ={ "",
		//	"-b",
		//	"-s","./GOPR2384_cab_hist_c3_src.txt",
		//	"-g","./GOPR2384_cab_hist_c3_gt.txt",				
		//	"-v","l"};

		//my_setting.input( 1+1+6 , setting_string );

		char *const setting_string[] ={ "",
			"-c",
			"-s","./GOPR2384_cab_hist_c3_src.txt",
			"-g","./GOPR2384_cab_hist_c3_gt.txt",				
			"-r","./list_res.txt",				
			"-v","l"};

		my_setting.input( 1+1+8 , setting_string );
	}
	else
		my_setting.input( argc, argv );

	if(my_setting.IF_FEAT)
	{
		return mainExtractFeat(my_setting);
		
	}

	if(my_setting.IF_TRAIN)
	{
		if(my_setting.IF_EXTRACT)
		{			
			return mainTrainWithExtract( my_setting );			
		}
		else
		{

		}
	}

	if(my_setting.IF_TEST)
	{
		if(my_setting.IF_EXTRACT)
		{			
			return mainTestWithExtract( my_setting );			
		}
		else
		{
		}
	}
	

	return 0;
}


void getFeature(LcInterface & my_setting, Mat & desc_all, Mat & lab_all)
{
	LcFeatureExtractor my_extractor;
	my_extractor.set_extractor( my_setting.feature_code_string );

	int n_all; //# of example on this cluster
	int n_sample; //# per frame
	
	int n_frm = (int)my_setting.src_name.size();
	int left = 0;
	int dim;
	for(int i = 0; i<n_frm ; i++)
	{
			
		Mat frm = imread(my_setting.src_name[i] );
		Mat gt = imread( my_setting.gt_name[i] );

		if(gt.type()==CV_8UC3)
		{
			cvtColor( gt,gt, CV_RGB2GRAY);
		}

		cv::resize( gt, gt, frm.size() );

		
		Mat desc, lab;
		my_extractor.work(frm, desc, gt, lab);		
		
		if(i==0)
		{
			getSampleSize( desc.rows, n_frm, n_all , n_sample );
			dim = desc.cols;
			desc_all = Mat::zeros( n_all, dim, 5);
			lab_all = Mat::zeros( n_all, 1 , 5);
		}			

		lc::swapMatrix( desc,  desc, lab, lab);

		int sample_now = n_sample;
		if( sample_now > desc.rows ) sample_now = desc.rows;

		int right = left + sample_now;

		desc.rowRange(0,sample_now).copyTo(desc_all.rowRange(left, right));
		lab.rowRange(0,sample_now).copyTo(lab_all.rowRange(left, right));

		left = right;			
	}

	desc_all = desc_all.rowRange( 0 , left);
	lab_all = lab_all.rowRange( 0 , left);
}


int mainTestWithExtract( LcInterface & my_setting )
{
	LcClassifier* my_classifier = my_setting.getClassifier();

	my_classifier->veb = 1;

	my_classifier->load( my_setting.classifier_savename );

	LcFeatureExtractor my_extractor;
	my_extractor.set_extractor( my_setting.feature_code_string );

	LcValidator report;

	for(int i = 0; i<(int)my_setting.src_name.size() ; i++)
	{
		Mat frm = imread(my_setting.src_name[i] );
		Mat gt = imread( my_setting.gt_name[i] );

		if(gt.type()==CV_8UC3)
		{
			cvtColor( gt,gt, CV_RGB2GRAY);
		}

		cv::resize( gt, gt, frm.size() );

		Mat desc,lab;
		my_extractor.work(frm, desc,gt,lab);		

		Mat res;
		LcValidator report_i = my_classifier->predict( desc, res, lab );

		report = report + report_i;

		Mat mask;

		getLabelMask(res, mask, frm.size(), my_extractor.bound_setting );

		vector<int> params;

		params.push_back( CV_IMWRITE_JPEG_QUALITY );
		params.push_back( 98 );

		
		//imshow( "debug",mask );
		//cv::waitKey(0);

		imwrite( my_setting.res_name[i], mask, params );

		cout << report.getF1() << endl;

	}

	//system("pause");

	return 0;
}

int mainTrainWithExtract( LcInterface & my_setting )
{	
	Mat desc_all;
	Mat lab_all;
	
	getFeature(my_setting, desc_all, lab_all);

	LcClassifier* my_classifier = my_setting.getClassifier();

	my_classifier->veb = 1;

	cout << desc_all.rows << " samples" << endl;

	my_classifier->train( desc_all , lab_all );

	my_classifier->save( my_setting.classifier_savename );

	//system("pause");

	return 0;
}



void getSampleSize( int rows, int n_frm, int & n_all , int & n_sample )
{
	int n = rows*n_frm;
	if( n < MAX_TRAIN_NUM )
	{
		n_all = n;
		n_sample = rows;
	}
	else
	{
		double rate = n / MAX_TRAIN_NUM;
		n_sample = int(floor( double(rows) / rate ));
		n_all = n_sample * n_frm;
	}
}

int mainExtractFeat(LcInterface & my_setting)
{
	LcFeatureExtractor my_extractor;
	my_extractor.set_extractor( my_setting.feature_code_string );

	for(int i = 0; i<(int)my_setting.src_name.size() ; i++)
	{
		Mat frm = imread(my_setting.src_name[i]);
		Mat desc;
		my_extractor.work(frm, desc );

		lc::LcMat2Bin( my_setting.feat_name[i].c_str(), desc );
	}
	//system("pause");
	return 0;
}

void getLabelMask( Mat & res, Mat & mask , Size size, int bound_setting)
{
	int _rows = size.height;
	int _cols = size.width;

	int _sz = (_rows - 2* bound_setting) * (_cols - 2* bound_setting);

	if(_sz != (int) res.rows)
	{
		cout << "size unmatch while get label mask" << endl;
		return;
	}

	mask = Mat::zeros( _rows, _cols, CV_8U);

	vector<KeyPoint> keypts;

	{
		DenseFeatureDetector dfd;

		float initFeatureScale = 1.f;		// inital size
		int featureScaleLevels = 1;		// one level
		float featureScaleMul = 1.00f;	// multiplier (ignored if only one level)
		int train_initXyStep = 1;		// space between pixels for training (must be 1)

		dfd = DenseFeatureDetector(initFeatureScale,featureScaleLevels,featureScaleMul,train_initXyStep,bound_setting,true,false);

		dfd.detect(mask,keypts);
	}

	//cout << "kp size " << keypts.size() << " res size " << res.rows << endl;
	
	for(int i = 0;i< (int)keypts.size();i++)
	{
		int r = int(floor(keypts[i].pt.y+.5));
		int c = int(floor(keypts[i].pt.x+.5));
		mask.at<unsigned char>(r,c) = (unsigned char)min( 255.0f, res.at<float>(i,0) * 255.0f);
	}
}
